/**
 * Copyright (c) 2016-2021, Bosco.Liao (bosco_liao@126.com).
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package org.iherus.shiro.redis.spring.boot.autoconfigure;

import org.iherus.shiro.cache.redis.Constant;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;

import java.time.Duration;
import java.util.Optional;

/**
 * 基础配置类
 *
 * @author Bosco.Liao
 * @since 2.0.0
 */
@ConfigurationProperties(prefix = "shiro.redis")
public class ShiroRedisProperties {

    private String host = "localhost";
    private int port = 6379;
    private int database;
    private String password;

    private SentinelConfig sentinel;

    private ClusterConfig cluster;

    private JedisConfig jedis = new JedisConfig();

    private LettuceConfig lettuce = new LettuceConfig();

    private RedissonConfig redisson = new RedissonConfig();

    private CacheConfig cache = new CacheConfig();

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    public int getDatabase() {
        return database;
    }

    public void setDatabase(int database) {
        this.database = database;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public SentinelConfig getSentinel() {
        return sentinel;
    }

    public void setSentinel(SentinelConfig sentinel) {
        this.sentinel = sentinel;
    }

    public ClusterConfig getCluster() {
        return cluster;
    }

    public void setCluster(ClusterConfig cluster) {
        this.cluster = cluster;
    }

    public JedisConfig getJedis() {
        return jedis;
    }

    public void setJedis(JedisConfig jedis) {
        this.jedis = jedis;
    }

    public LettuceConfig getLettuce() {
        return lettuce;
    }

    public void setLettuce(LettuceConfig lettuce) {
        this.lettuce = lettuce;
    }

    public RedissonConfig getRedisson() {
        return redisson;
    }

    public void setRedisson(RedissonConfig redisson) {
        this.redisson = redisson;
    }

    public CacheConfig getCache() {
        return cache;
    }

    public void setCache(CacheConfig cache) {
        this.cache = cache;
    }

    public static class SentinelConfig {

        private String masterName;
        private String nodes;

        public String getMasterName() {
            return masterName;
        }

        public void setMasterName(String masterName) {
            this.masterName = masterName;
        }

        public String getNodes() {
            return nodes;
        }

        public void setNodes(String nodes) {
            this.nodes = nodes;
        }

    }

    public static class ClusterConfig {

        private String nodes;
        private Integer maxAttempts = 5;

        public String getNodes() {
            return nodes;
        }

        public void setNodes(String nodes) {
            this.nodes = nodes;
        }

        public Integer getMaxAttempts() {
            return maxAttempts;
        }

        public void setMaxAttempts(Integer maxAttempts) {
            this.maxAttempts = Optional.ofNullable(maxAttempts).orElse(5);
        }
    }

    public static class JedisConfig {
        private String clientName;
        private Duration soTimeout = Duration.ofMillis(2000);
        private Duration connectTimeout = Duration.ofMillis(2000);
        private boolean ssl;

        private Pool pool = new Pool();

        public String getClientName() {
            return clientName;
        }

        public void setClientName(String clientName) {
            this.clientName = clientName;
        }

        public Duration getSoTimeout() {
            return soTimeout;
        }

        public void setSoTimeout(Duration soTimeout) {
            this.soTimeout = soTimeout;
        }

        public Duration getConnectTimeout() {
            return connectTimeout;
        }

        public void setConnectTimeout(Duration connectTimeout) {
            this.connectTimeout = connectTimeout;
        }

        public boolean isSsl() {
            return ssl;
        }

        public void setSsl(boolean ssl) {
            this.ssl = ssl;
        }

        public Pool getPool() {
            return pool;
        }

        public void setPool(Pool pool) {
            this.pool = pool;
        }

    }

    public static class LettuceConfig {

        private String clientName;
        private String readFrom;
        private boolean ssl;
        private boolean verifyPeer = true;
        private boolean startTls;

        private Pool pool = new Pool();

        public String getClientName() {
            return clientName;
        }

        public void setClientName(String clientName) {
            this.clientName = clientName;
        }

        public String getReadFrom() {
            return readFrom;
        }

        public void setReadFrom(String readFrom) {
            this.readFrom = readFrom;
        }

        public boolean isSsl() {
            return ssl;
        }

        public void setSsl(boolean ssl) {
            this.ssl = ssl;
        }

        public boolean isVerifyPeer() {
            return verifyPeer;
        }

        public void setVerifyPeer(boolean verifyPeer) {
            this.verifyPeer = verifyPeer;
        }

        public boolean isStartTls() {
            return startTls;
        }

        public void setStartTls(boolean startTls) {
            this.startTls = startTls;
        }

        public Pool getPool() {
            return pool;
        }

        public void setPool(Pool pool) {
            this.pool = pool;
        }
    }

    public static class RedissonConfig {

        private String clientName;
        private boolean ssl;
        private Duration connectTimeout = Duration.ofMillis(10000);
        private Duration soTimeout = Duration.ofMillis(3000);
        private Integer connectionMinIdleSize = 8;
        private Integer connectionPoolSize = 32;

        public String getClientName() {
            return clientName;
        }

        public void setClientName(String clientName) {
            this.clientName = clientName;
        }

        public boolean isSsl() {
            return ssl;
        }

        public void setSsl(boolean ssl) {
            this.ssl = ssl;
        }

        public Duration getConnectTimeout() {
            return connectTimeout;
        }

        public void setConnectTimeout(Duration connectTimeout) {
            this.connectTimeout = connectTimeout;
        }

        public Duration getSoTimeout() {
            return soTimeout;
        }

        public void setSoTimeout(Duration soTimeout) {
            this.soTimeout = soTimeout;
        }

        public Integer getConnectionMinIdleSize() {
            return connectionMinIdleSize;
        }

        public void setConnectionMinIdleSize(Integer connectionMinIdleSize) {
            this.connectionMinIdleSize = Optional.ofNullable(connectionMinIdleSize).orElse(8);
        }

        public Integer getConnectionPoolSize() {
            return connectionPoolSize;
        }

        public void setConnectionPoolSize(Integer connectionPoolSize) {
            this.connectionPoolSize = Optional.ofNullable(connectionPoolSize).orElse(32);
        }
    }

    public static class Pool {

        private Integer maxTotal = 8;
        private Integer minIdle = 3;
        private Integer maxIdle = 8;
        private Long maxWaitMillis = 90000L;
        private boolean testOnBorrow;
        private boolean testWhileIdle = true;
        private Long timeBetweenEvictionRunsMillis = 30000L;
        private Long minEvictableIdleTimeMillis = 60000L;
        private Integer numTestsPerEvictionRun = -1;

        public Integer getMaxTotal() {
            return maxTotal;
        }

        public void setMaxTotal(Integer maxTotal) {
            this.maxTotal = Optional.ofNullable(maxTotal).orElse(8);
        }

        public Integer getMaxIdle() {
            return maxIdle;
        }

        public void setMaxIdle(Integer maxIdle) {
            this.maxIdle = Optional.ofNullable(maxIdle).orElse(8);
        }

        public Integer getMinIdle() {
            return minIdle;
        }

        public void setMinIdle(Integer minIdle) {
            this.minIdle = Optional.ofNullable(minIdle).orElse(3);
        }

        public Long getMaxWaitMillis() {
            return maxWaitMillis;
        }

        public void setMaxWaitMillis(Long maxWaitMillis) {
            this.maxWaitMillis = Optional.ofNullable(maxWaitMillis).orElse(90000L);
        }

        public boolean isTestOnBorrow() {
            return testOnBorrow;
        }

        public void setTestOnBorrow(boolean testOnBorrow) {
            this.testOnBorrow = testOnBorrow;
        }

        public boolean isTestWhileIdle() {
            return testWhileIdle;
        }

        public void setTestWhileIdle(boolean testWhileIdle) {
            this.testWhileIdle = testWhileIdle;
        }

        public Long getTimeBetweenEvictionRunsMillis() {
            return timeBetweenEvictionRunsMillis;
        }

        public void setTimeBetweenEvictionRunsMillis(Long timeBetweenEvictionRunsMillis) {
            this.timeBetweenEvictionRunsMillis = Optional.ofNullable(timeBetweenEvictionRunsMillis).orElse(30000L);
        }

        public Long getMinEvictableIdleTimeMillis() {
            return minEvictableIdleTimeMillis;
        }

        public void setMinEvictableIdleTimeMillis(Long minEvictableIdleTimeMillis) {
            this.minEvictableIdleTimeMillis = Optional.ofNullable(minEvictableIdleTimeMillis).orElse(60000L);
        }

        public Integer getNumTestsPerEvictionRun() {
            return numTestsPerEvictionRun;
        }

        public void setNumTestsPerEvictionRun(Integer numTestsPerEvictionRun) {
            this.numTestsPerEvictionRun = Optional.ofNullable(numTestsPerEvictionRun).orElse(-1);
        }
    }

    public static class CacheConfig {
        private String keyPrefix = Constant.DEFAULT_CACHE_KEY_PREFIX;
        private Duration expiration = Constant.DEFAULT_CACHE_EXPIRATION;
        private Integer database;
        private BatchOptionsConfig batchOptions = new BatchOptionsConfig();

        public String getKeyPrefix() {
            return keyPrefix;
        }

        public void setKeyPrefix(String keyPrefix) {
            this.keyPrefix = keyPrefix;
        }

        public Duration getExpiration() {
            return expiration;
        }

        public void setExpiration(Duration expiration) {
            this.expiration = expiration;
        }

        public Integer getDatabase() {
            return database;
        }

        public void setDatabase(Integer database) {
            this.database = database;
        }

        public BatchOptionsConfig getBatchOptions() {
            return batchOptions;
        }

        public void setBatchOptions(BatchOptionsConfig batchOptions) {
            this.batchOptions = batchOptions;
        }
    }

    public static class BatchOptionsConfig {

        private Integer scanBatchSize = Constant.DEFAULT_SCAN_BATCH_SIZE;
        private Integer deleteBatchSize = Constant.DEFAULT_DEL_BATCH_SIZE;
        private Integer fetchBatchSize = Constant.DEFAULT_FETCH_BATCH_SIZE;

        public Integer getScanBatchSize() {
            return scanBatchSize;
        }

        public void setScanBatchSize(Integer scanBatchSize) {
            this.scanBatchSize = Optional.ofNullable(scanBatchSize).orElse(Constant.DEFAULT_SCAN_BATCH_SIZE);
        }

        public Integer getDeleteBatchSize() {
            return deleteBatchSize;
        }

        public void setDeleteBatchSize(Integer deleteBatchSize) {
            this.deleteBatchSize = Optional.ofNullable(deleteBatchSize).orElse(Constant.DEFAULT_DEL_BATCH_SIZE);
        }

        public Integer getFetchBatchSize() {
            return fetchBatchSize;
        }

        public void setFetchBatchSize(Integer fetchBatchSize) {
            this.fetchBatchSize = Optional.ofNullable(fetchBatchSize).orElse(Constant.DEFAULT_FETCH_BATCH_SIZE);
        }
    }

}